package com.jprocms.module.cms.controller.admin.content;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.jpro.framework.common.pojo.CommonResult;
import com.jpro.framework.common.pojo.PageParam;
import com.jpro.framework.common.pojo.PageResult;
import com.jpro.framework.common.util.json.JsonUtils;
import com.jpro.framework.common.util.string.StrUtils;
import com.jpro.framework.datapermission.core.annotation.DataPermission;
import com.jpro.framework.security.core.util.SecurityFrameworkUtils;
import com.jprocms.module.cms.config.JproProperties;
import com.jprocms.module.cms.controller.admin.content.vo.*;
import com.jprocms.module.cms.convert.content.ContentConvert;
import com.jprocms.module.cms.dal.dataobject.content.ContentDO;
import com.jprocms.module.cms.dal.dataobject.site.SiteDO;
import com.jprocms.module.cms.enums.EnumContentStatus;
import com.jprocms.module.cms.es.dto.ContentAdminQueryDto;
import com.jprocms.module.cms.es.dto.ContentIndexResetDto;
import com.jprocms.module.cms.es.service.ContentEsService;
import com.jprocms.module.cms.framework.cache.CacheService;
import com.jprocms.module.cms.service.content.ContentService;
import com.jprocms.module.cms.service.content.SensitiveWordService;
import com.jprocms.module.cms.util.CmsContextUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.jpro.framework.common.pojo.CommonResult.success;
import static com.jprocms.module.cms.config.ThreadPoolConfiguration.CMS_INDEX_THREAD_POOL_TASK_EXECUTOR;
import static com.jprocms.module.cms.dal.redis.RedisKeyConstants.*;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.CONTENT_NOT_EXISTS;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.SENSITIVEWORD_TIP;

@Slf4j
@Tag(name = "管理后台 - 内容")
@RestController
@RequestMapping("/cms/content")
@Validated
public class ContentController {

    //预览初始ID
    private final static Integer PREVIEW_CONTENT_INIT = 1000000000;
    @Resource
    private ContentService contentService;
    @Resource
    private JproProperties jproProperties;
    @Resource
    private ContentEsService contentEsService;
    @Resource(name = CMS_INDEX_THREAD_POOL_TASK_EXECUTOR)
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private SensitiveWordService sensitiveWordService;
    @Resource
    private CacheService cacheService;

    @PostMapping("/create")
    @Operation(summary = "创建内容")
    @PreAuthorize("@ss.hasPermission('cms:content:create')")
    public CommonResult<Long> createContent(@Valid @RequestBody ContentCreateReqVO createReqVO) {
        createReqVO.setLivePreview(false);
        Long contentId = contentService.createContent(createReqVO);
        return success(contentId);
    }

    @PostMapping("/createPreview")
    @Operation(summary = "创建实施预览内容")
    @PreAuthorize("@ss.hasPermission('cms:content:query')")
    public CommonResult<Long> createPreviewContent(@Valid @RequestBody ContentCreateReqVO createReqVO) {
        createReqVO.setLivePreview(true);
        //从缓存中获取最新的实时预览id
        RLock lock = redissonClient.getLock(LIVE_PREVIEW_LOCK);
        try {
            lock.lock(LIVE_PREVIEW_LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
            Integer previewContentId = null;
            Object preCacheContentId = cacheService.getCacheObject(LIVE_PREVIEW_CONTENTID);
            if (preCacheContentId != null) {
                previewContentId = Integer.parseInt(preCacheContentId.toString());
            }
            if (previewContentId == null) {
                previewContentId = PREVIEW_CONTENT_INIT;
            } else {
                previewContentId = previewContentId + 1;
            }
            cacheService.setCacheObject(LIVE_PREVIEW_CONTENTID, previewContentId);
            createReqVO.setId(Long.parseLong(previewContentId.toString()));
            Long contentId = contentService.createContent(createReqVO);
            return success(contentId);
        } finally {
            lock.unlock();
        }
    }

    @PostMapping("/update")
    @Operation(summary = "更新内容")
    @PreAuthorize("@ss.hasPermission('cms:content:update')")
    public CommonResult<Boolean> updateContent(@Valid @RequestBody ContentUpdateReqVO updateReqVO) {
        contentService.updateContent(updateReqVO);
        return success(true);
    }

    @PostMapping("/checkContentSensitiveWord")
    @Operation(summary = "完整检查内容是否有敏感词")
    @PreAuthorize("@ss.hasPermission('cms:content:create')")
    public CommonResult<Boolean> checkContentSensitiveWord(@Valid @RequestBody ContentBaseVO contentBaseVO) {
        final Set<String> sensitiveWords = sensitiveWordService.checkSensitiveWord(contentBaseVO);
        if (CollUtil.isNotEmpty(sensitiveWords)) {
            throw exception0(SENSITIVEWORD_TIP.getCode(), StrUtil.format("存在敏感词({})", JsonUtils.toJsonString(sensitiveWords)));
        }
        return success(true);
    }

    @PostMapping("/checkSensitiveWord")
    @Operation(summary = "检查单个字段是否有敏感词")
    @PermitAll
    public CommonResult<Set<String>> checkSensitiveWord(@Valid @RequestBody SensitiveWordCheckVO wordCheckVO) {
        final Set<String> sensitiveWords = sensitiveWordService.checkSensitiveWord(wordCheckVO);
        return success(sensitiveWords);
    }

    @PostMapping("/delete")
    @Operation(summary = "删除内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:delete')")
    public CommonResult<Boolean> deleteContent(@RequestBody List<Long> ids) {
        contentService.batchDeleteContent(ids);
        return success(true);
    }

    @PostMapping("/archive")
    @Operation(summary = "归档内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:archive')")
    public CommonResult<Boolean> archiveContent(@RequestBody List<Long> ids) {
        contentService.batchArchiveContent(ids);
        return success(true);
    }


    @PostMapping("/twoDelete")
    @Operation(summary = "彻底删除内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:delete')")
    public CommonResult<Boolean> twoDeleteContent(@RequestBody List<Long> ids) {
        contentService.batchTwoDeleteContent(ids);
        return success(true);
    }

    @PostMapping("/top")
    @Operation(summary = "置顶")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:top')")
    public CommonResult<Boolean> topContent(@Valid @RequestBody ContentTopVo topVo) {
        contentService.batchTopContent(topVo);
        return success(true);
    }

    @PostMapping("/move")
    @Operation(summary = "移动")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:move')")
    public CommonResult<Boolean> moveContent(@Valid @RequestBody ContentMoveVo moveVo) {
        contentService.batchMoveContent(moveVo);
        return success(true);
    }

    @PostMapping("/mark")
    @Operation(summary = "标记")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:mark')")
    public CommonResult<Boolean> markContent(@Valid @RequestBody ContentMarkVo markVo) {
        contentService.batchMarkContent(markVo);
        return success(true);
    }

    @PostMapping("/block")
    @Operation(summary = "设置区块")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:block')")
    public CommonResult<Boolean> blockContent(@Valid @RequestBody ContentBlockVo blockVo) {
        contentService.batchBlockContent(blockVo);
        return success(true);
    }

    @PostMapping("/publish")
    @Operation(summary = "发布内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:publish')")
    public CommonResult<Boolean> publish(@RequestBody List<Long> ids) {
        contentService.batchPublish(ids);
        return success(true);
    }

    @PostMapping("/offline")
    @Operation(summary = "下线内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:offline')")
    public CommonResult<Boolean> offline(@Valid @RequestBody List<Long> ids) {
        contentService.batchOffline(ids);
        return success(true);
    }


    @PostMapping("/copy")
    @Operation(summary = "复制内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:copy')")
    public CommonResult<Boolean> copy(@Valid @RequestBody ContentCopyVo copyVo) {
        final SiteDO site = CmsContextUtil.getSite();
        contentService.batchCopyContent(copyVo, site.getPushSubmitWorkflow());
        return success(true);
    }

    @PostMapping("/push")
    @Operation(summary = "推送内容")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:push')")
    public CommonResult<Boolean> push(@Valid @RequestBody ContentPushVo pushVo) {
        contentService.batchPushContent(pushVo);
        return success(true);
    }


    @PostMapping("/submit")
    @Operation(summary = "提交审核")
    @Parameter(name = "ids", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('cms:content:submit')")
    public CommonResult<Boolean> submit(@RequestBody @Valid ContentSubmitVO reqDTO) {
        contentService.submit(reqDTO);
        return success(true);
    }

    @PostMapping("/approve")
    @Operation(summary = "审核通过")
    @PreAuthorize("@ss.hasPermission('cms:content:approve')")
    public CommonResult<Boolean> approve(@Valid @RequestBody ContentTaskApproveReqVO reqVO) {
        contentService.approve(reqVO);
        return success(true);
    }

    @PostMapping("/reject")
    @Operation(summary = "审核不通过")
    @PreAuthorize("@ss.hasPermission('cms:content:reject')")
    public CommonResult<Boolean> reject(@Valid @RequestBody ContentTaskRejectReqVO reqVO) {
        contentService.reject(reqVO);
        return success(true);
    }

    @PostMapping("/updateAndSubmit")
    @Operation(summary = "更新内容")
    @PreAuthorize("@ss.hasPermission('cms:content:submit')")
    public CommonResult<Boolean> updateContentAndSubmit(@Valid @RequestBody ContentUpdateReqVO updateReqVO) {
        contentService.updateContentAndSubmit(updateReqVO);
        return success(true);
    }

    @PostMapping("/createAndSubmit")
    @Operation(summary = "创建内容并提交")
    @PreAuthorize("@ss.hasPermission('cms:content:create')")
    public CommonResult<Boolean> createContentAndSubmit(@Valid @RequestBody ContentCreateReqVO createReqVO) {
        createReqVO.setLivePreview(false);
        contentService.createContentAndSubmit(createReqVO);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得内容")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('cms:content:query')")
    public CommonResult<ContentRespVO> getContent(@RequestParam("id") Long id, HttpServletRequest request) {
        ContentDO content = contentService.getContent(id);
        Assert.notNull(content, () -> {
            throw exception(CONTENT_NOT_EXISTS);
        });
        return success(ContentConvert.INSTANCE.convertMore(content));
    }

    @GetMapping("/page")
    @Operation(summary = "获得内容分页")
    @DataPermission(enable = false) //查询方法已单独处理权限查询
    @PreAuthorize("@ss.hasPermission('cms:content:query')")
    public CommonResult<PageResult<ContentRespVO>> getContentPage(@Valid ContentPageReqVO pageVO, HttpServletRequest request) {
        pageVO.setSiteId(CmsContextUtil.getCurrSiteId());
        PageResult<ContentDO> pageResult = contentService.getContentPage(pageVO);
        return success(ContentConvert.INSTANCE.convertPage(pageResult));
    }

    @GetMapping("/todopage")
    @Operation(summary = "获得待审核内容分页")
    @PreAuthorize("@ss.hasPermission('cms:content:query')")
    public CommonResult<PageResult<ContentRespVO>> getTodoContentPage(@Valid PageParam pageVO, HttpServletRequest request) {
        PageResult<ContentDO> pageResult = contentService.getTodoContentPage(pageVO);
        return success(ContentConvert.INSTANCE.convertPage(pageResult));
    }

    @PermitAll
    @GetMapping("/toauditContentCount")
    @Operation(summary = "获得待审核内容总数")
    public CommonResult<Long> getTodoContentCount() {
        final Long todoContentCount = contentService.getTodoContentCount();
        return success(todoContentCount);
    }


    @GetMapping(value = "/index/page")
    @Operation(summary = "内容索引分页")
    public CommonResult<PageResult<ContentIndexRecordDto>> indexPage(@ModelAttribute ContentAdminQueryDto dto) throws IOException {
        dto.setStatus(EnumContentStatus.PUBLISHED.getStatus());
        dto.setSiteId(CmsContextUtil.getCurrSiteId());
        if (StringUtils.isNotBlank(dto.getQ())) {
            dto.setKeyWords(StrUtils.splitToString(dto.getQ()));
        }
        PageResult<ContentIndexRecordDto> pageResult = contentEsService.getContentPageAmin(dto);
        List<ContentIndexRecordDto> recordDtos = pageResult.getList();
        if (CollectionUtils.isNotEmpty(recordDtos)) {
            for (int i = 0; i < recordDtos.size(); i++) {
                ContentIndexRecordDto item = recordDtos.get(i);
                final ContentDO contentDO = contentService.getContent(item.getId());
                if (contentDO != null) {
                    item.setChannelName(contentDO.getChannelName());
                    item.setSiteName(contentDO.getSiteName());
                    item.setUrl(contentDO.getUrl());
                }
            }
        }
        return success(pageResult);
    }

    @PostMapping(value = "/index/reset")
    @Operation(summary = "内容索引重新生成")
    public CommonResult<Boolean> resetIndex(@Valid @RequestBody ContentIndexResetDto dto) {
        log.info("内容索引重新生成,操作者ID {}", SecurityFrameworkUtils.getLoginUser().getId());
        threadPoolTaskExecutor.execute(
                () -> {
                    contentEsService.resetContentIndex(dto);
                }
        );
        return success(true);
    }

    @GetMapping("/getContentViewPage")
    @Operation(summary = "获得内容浏览量分页")
    @DataPermission(enable = false) //查询方法已单独处理权限查询
    @PreAuthorize("@ss.hasPermission('cms:content:static:query')")
    public CommonResult<PageResult<ContentTopViewRespVO>> getContentViewPage(@Valid ContentPageReqVO pageVO, HttpServletRequest request) {
        pageVO.setSiteId(CmsContextUtil.getCurrSiteId());
        PageResult<ContentDO> pageResult = contentService.getContentPageIgnorePerm(pageVO);
        PageResult<ContentTopViewRespVO> pageRespResult = new PageResult<>();
        final List<ContentDO> list = pageResult.getList();
        List<ContentTopViewRespVO> respVOS = new ArrayList<>();
        for (ContentDO contentDO : list) {
            respVOS.add(ContentConvert.INSTANCE.convertToTopViewResp(contentDO));
        }
        pageRespResult.setList(respVOS);
        pageRespResult.setPages(pageResult.getPages());
        pageRespResult.setCurrent(pageResult.getCurrent());
        pageRespResult.setTotal(pageResult.getTotal());
        return success(pageRespResult);
    }

    @GetMapping("/getConvertEnabled")
    @Operation(summary = "文档服务是否开启")
    @PreAuthorize("@ss.hasPermission('cms:content:query')")
    public CommonResult<Boolean> getConvertEnabled() {
        return success(jproProperties.isConvertEnable());
    }

    @GetMapping("/getPublishMultiChannelEnabled")
    @Operation(summary = "获取开启发布内容可引用多栏目")
    @PreAuthorize("@ss.hasPermission('cms:content:query')")
    public CommonResult<Boolean> getPublishMultiChannel() {
        return success(jproProperties.isPublishMultiChannel());
    }
}
